package com.ccx.myinterpreter;

import java.util.List;

import com.ccx.myinterpreter.Expr.Variable;

abstract class Stmt {
    public interface Visitor {
        void visitExpr(Expression stmt);
        void visitPrint(Print stmt);
        void visitVarDecl(VarDecl stmt);
        void visitBlock(Block block);
        void visitIf(IfStmt ifStmt);
        void visitWhileStmt(WhileStmt whileStmt);
        void visitForStmt(forStmt forStmt);
        void visitFunDecl(FunDecl funDecl);
        void visitReturnStmt(ReturnStmt returnStmt);
        void visitClassDecl(ClassDecl classDecl);
    }

    static class Expression extends Stmt {
        final Expr expr;

        public Expression(Expr expr) {
            this.expr = expr;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitExpr(this);
        }
    }

    static class Print extends Stmt {
        final Expr expr;

        public Print(Expr expr) {
            this.expr = expr;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitPrint(this);
        }
    }

    static class VarDecl extends Stmt {
        final Token identifier;
        final Expr expr;

        public VarDecl(Token identifier, Expr expr) {
            this.identifier = identifier;
            this.expr = expr;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitVarDecl(this);
        }
    }

    static class Block extends Stmt {
        final List<Stmt> stmts;
        public Block(List<Stmt> stmts) {
            this.stmts = stmts;
        }
        @Override
        void accept(Visitor visitor) {
            visitor.visitBlock(this);
        }
    }

    static class IfStmt extends Stmt {
        
        final Expr expr;
        final Stmt thenStmt;
        final Stmt elseStmt;

        public IfStmt(Expr expr, Stmt thenStmt, Stmt elseStmt) {
            this.expr = expr;
            this.thenStmt = thenStmt;
            this.elseStmt = elseStmt;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitIf(this);
        }
        
    }

    static class WhileStmt extends Stmt {

        final Expr condiation;
        final Stmt stmt;
        
        public WhileStmt(Expr condiation, Stmt stmt) {
            this.condiation = condiation;
            this.stmt = stmt;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitWhileStmt(this);
        }

    }

    static class forStmt extends Stmt {

        final Stmt first;
        final Expr second;
        final Expr third;
        final Stmt body;

        public forStmt(Stmt first, Expr second, Expr third, Stmt body) {
            this.first = first;
            this.second = second;
            this.third = third;
            this.body = body;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitForStmt(this);
        }
        
    }

    static class FunDecl extends Stmt {

        final Token name;
        final List<Token> params;
        final Stmt.Block body;

        public FunDecl(Token name, List<Token> params, Stmt.Block body) {
            this.name = name;
            this.params = params;
            this.body = body;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitFunDecl(this);
        }
        
    }

    static class ReturnStmt extends Stmt {
        final Expr expr;

        public ReturnStmt(Expr expr) {
            this.expr = expr;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitReturnStmt(this);
        }
        
    }

    static class ClassDecl extends Stmt {

        final Token name;
        final List<FunDecl> methods;
        final Variable superClass;

        public ClassDecl(Token name, List<FunDecl> methods, Variable supperClass) {
            this.name = name;
            this.methods = methods;
            this.superClass = supperClass;
        }

        @Override
        void accept(Visitor visitor) {
            visitor.visitClassDecl(this);
        }
        
    }

    abstract void accept(Visitor visitor);
}
